﻿using System.Collections.Generic;
using System.IO;
using System;

namespace TinyAsset
{
    public abstract class AbstractFileEntity
    {
        public string name;

        public long size;

        public string hash;

        public byte location;

        public byte type;

        public virtual void Serialize(BinaryWriter writer)
        {
            writer.Write(name);
            writer.Write(size);
            writer.Write(hash);
            writer.Write(location);
            writer.Write(type);
        }

        public virtual void Deserialize(BinaryReader reader)
        {
            name = reader.ReadString();
            size = reader.ReadInt64();
            hash = reader.ReadString();
            location = reader.ReadByte();
            type = reader.ReadByte();
        }
    }

    public class BundledAssetKey
    {
        public string name;

        public int dir;

        public int bundle;

        public void Serialize(BinaryWriter writer)
        {
            writer.Write(name);
            writer.Write(dir);
            writer.Write(bundle);
        }

        public void Deserialize(BinaryReader reader)
        {
            name = reader.ReadString();
            dir = reader.ReadInt32();
            bundle = reader.ReadInt32();
        }
    }

    public class NativeAssetEntity : AbstractFileEntity
    {
        public NativeAssetEntity() { type = 0; }
    }

    public class AssetBundleEntity : AbstractFileEntity
    {
        public List<int> dependencies;

        public AssetBundleEntity() { type = 1; }

        public override void Serialize(BinaryWriter writer)
        {
            base.Serialize(writer);
            int count = dependencies == null ? 0 : dependencies.Count;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                writer.Write(dependencies[i]);
        }

        public override void Deserialize(BinaryReader reader)
        {
            base.Deserialize(reader);
            int count = reader.ReadInt32();
            dependencies = new List<int>(count);
            for (int i = 0; i < count; i++)
                dependencies[i] = reader.ReadInt32();
        }
    }

    public class FileEntityGroup
    {
        public string name;

        public List<int> assetBundleEntities;

        public List<int> nativeAssetEntities;
        
        public void Serialize(BinaryWriter writer)
        {
            writer.Write(name);

            int count = assetBundleEntities == null ? 0 : assetBundleEntities.Count;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                writer.Write(assetBundleEntities[i]);

            count = nativeAssetEntities == null ? 0 : nativeAssetEntities.Count;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                writer.Write(nativeAssetEntities[i]);
        }

        public void Deserialize(BinaryReader reader)
        {
            name = reader.ReadString();

            int count = reader.ReadInt32();
            assetBundleEntities = new List<int>(count);
            for (int i = 0; i < count; i++)
                assetBundleEntities[i] = reader.ReadInt32();

            count = reader.ReadInt32();
            nativeAssetEntities = new List<int>(count);
            for (int i = 0; i < count; i++)
                nativeAssetEntities[i] = reader.ReadInt32();
        }
    }

    public class Manifest
    {
        public Version appVersion;

        public Version resVersion;

        public string[] dirs;

        public BundledAssetKey[] bundledAssetKeys;

        public NativeAssetEntity[] nativeAssetEntities;

        public AssetBundleEntity[] assetBundleEntities;

        public FileEntityGroup[] fileEntityGroups;

        readonly Dictionary<string, FileEntityGroup> _groups = new Dictionary<string, FileEntityGroup>();

        readonly Dictionary<string, AbstractFileEntity> _fileEntres = new Dictionary<string, AbstractFileEntity>();

        public void Serialize(BinaryWriter writer)
        {
            writer.Write(appVersion.ToString());
            writer.Write(resVersion.ToString());

            int count = dirs == null ? 0 : dirs.Length;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                writer.Write(dirs[i]);

            count = bundledAssetKeys == null ? 0 : bundledAssetKeys.Length;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                bundledAssetKeys[i].Serialize(writer);

            count = nativeAssetEntities == null ? 0 : nativeAssetEntities.Length;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                nativeAssetEntities[i].Serialize(writer);

            count = assetBundleEntities == null ? 0 : nativeAssetEntities.Length;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                assetBundleEntities[i].Serialize(writer);

            count = fileEntityGroups.Length;
            writer.Write(count);
            for (int i = 0; i < count; i++)
                fileEntityGroups[i].Serialize(writer);
        }

        public void Deserialize(BinaryReader reader)
        {
            appVersion = new Version(reader.ReadString());
            resVersion = new Version(reader.ReadString());

            int count = reader.ReadInt32();
            dirs = new string[count];
            for (int i = 0; i < dirs.Length; i++)
                dirs[i] = reader.ReadString();

            count = reader.ReadInt32();
            bundledAssetKeys = new BundledAssetKey[count];
            for (int i = 0; i < count; i++)
            {
                bundledAssetKeys[i] = new BundledAssetKey();
                bundledAssetKeys[i].Deserialize(reader);
            }

            count = reader.ReadInt32();
            nativeAssetEntities = new NativeAssetEntity[count];
            for (int i = 0; i < count; i++)
            {
                nativeAssetEntities[i] = new NativeAssetEntity();
                nativeAssetEntities[i].Deserialize(reader);
                _fileEntres.Add(nativeAssetEntities[i].name, nativeAssetEntities[i]);
            }

            count = reader.ReadInt32();
            assetBundleEntities = new AssetBundleEntity[count];
            for (int i = 0; i < count; i++)
            {
                assetBundleEntities[i] = new AssetBundleEntity();
                assetBundleEntities[i].Deserialize(reader);
                _fileEntres.Add(assetBundleEntities[i].name, assetBundleEntities[i]);
            }

            count = reader.ReadInt32();
            fileEntityGroups = new FileEntityGroup[count];
            for (int i = 0; i < count; i++)
            {
                fileEntityGroups[i] = new FileEntityGroup();
                fileEntityGroups[i].Deserialize(reader);
                _groups.Add(fileEntityGroups[i].name, fileEntityGroups[i]);
            }
        }

        public AssetBundleEntity GetAssetBundleEntity(string name)
        {
            AbstractFileEntity entity = GetFileEntity(name);
            return entity == null ? null : entity as AssetBundleEntity;
        }

        public NativeAssetEntity GetNativeAssetEntity(string name)
        {
            AbstractFileEntity entity = GetFileEntity(name);
            return entity == null ? null : entity as NativeAssetEntity;
        }

        public AbstractFileEntity GetFileEntity(string name)
        {
            AbstractFileEntity fileEntity;
            _fileEntres.TryGetValue(name, out fileEntity);
            return fileEntity;
        }

        public List<AbstractFileEntity> GetFileEntitiesByGroupName(string name)
        {
            List<AbstractFileEntity> abstractFiles = new List<AbstractFileEntity>();
            if(_groups.TryGetValue(name,out FileEntityGroup group))
            {
                foreach (var item in group.assetBundleEntities)
                    abstractFiles.Add(assetBundleEntities[item]);
                foreach (var item in group.nativeAssetEntities)
                    abstractFiles.Add(nativeAssetEntities[item]);
            }
            return abstractFiles;
        }
    }
}